require 'rest-client'
require 'json'
require 'base64'
require 'pronto'
require 'pronto/rubocop'

class RubocopAnalysis
  def initialize(repo, head_sha, bare_sha, pr_iid, access_token_str)

    @repo = repo
    @access_token = Base64.decode64(access_token_str)[0...32]
    @head_sha = head_sha
    @bare_sha = bare_sha
    @pr_iid = pr_iid
  end

  def exec
    create_rubocop
    initiate_check_run
  end

  def create_rubocop
    data = {
      access_token: @access_token,
      details_url: 'https://gitee.com/liwen',
      name: 'Rubocop 检测',
      head_sha: @head_sha
    }
    url = "https://gitee.com/api/v5/repos/#{@repo}/check-runs"
    @response_json = RestClient.post url, data
    @response = JSON.parse(@response_json)
    @check_run_id = @response['id']
    puts "@check_run_id => #{@check_run_id}"
  end

  def initiate_check_run
    url = "https://gitee.com/api/v5/repos/#{@repo}/check-runs/#{@check_run_id}"
    data = {
      access_token: @access_token,
      status: 'in_progress'
    }
    RestClient.patch url, data
    analyse_and_finished_check
  end

  def analyse_and_finished_check
    puts "begining analyse repo"
    files = ::Pronto.run('origin/master', '.', [Pronto::Formatter::JsonFormatter.new])
    max_annotations = 70
    annotations = []
    suggestions = []

    if files.count == 0
      conclusion = 'success'
    else
      conclusion = 'success'
      files.each_with_index do |file, index|
        break if annotations.length > max_annotations
        annotation_level_list = {
          warning: "warning",
          error: "failure",
          fatal: "failure"
        }

        if ["error", "fatal"].include?(file.level)
          conclusion = "failure"
        elsif conclusion == 'success' && "warning" == file.level.to_s
          conclusion = "neutral"
        end

        start_line = file.line.new_lineno
        end_line = file.line.new_lineno
        message, *suggestion = file.msg.split("\n")
        annotation_level = annotation_level_list[file.level]
        unless suggestion.empty?
          body = "Rubocop suggestion#{suggestion.join("\n")}"
          can_insert, suggestion = can_insert_suggestion?(file, body)
          suggestions.push(suggestion) if can_insert
        end

        annotation = {
          path: file.path,
          start_line: start_line,
          end_line: end_line,
          annotation_level: annotation_level,
          message: message
        }

        annotations.push(annotation)
      end
    end
    summary = "Pronto runner for Rubocop, ruby code analyzer"
    text = "只检测 warning/error/fatal 三个级别的问题, 最多70个代码注释"

    url = "https://gitee.com/api/v5/repos/#{@repo}/check-runs/#{@check_run_id}"
    annotations = annotations[0..2]
    data = {
      access_token: @access_token,
      conclusion: conclusion,
      details_url: "https://github.com/rubocop/rubocop",
      output: {
        title: 'Octo RuboCop',
        summary: summary,
        text: text,
        annotations: annotations
      }
    }
    puts "finished analyse, completed check run"
    reposponse = RestClient.patch url, data
    post_suggestions(suggestions) if suggestions.count > 0
    puts "finished..."
  end

  def can_insert_suggestion?(file, body)
    suggestion = {
      body: body,
      access_token: @access_token,
      path: file.path,
      # position: file.line.commit_line.position,
      position: file.line.commit_line.position + 1,
      commit_id: file.commit_sha
    }
    if get_exists_suggestions.find{|s| s[:path] == file.path && file.msg.include?(s[:body][20..-1]) && s[:commit_id] == file.commit_sha && s[:new_line] == file.line.new_lineno}.nil?
      return [true, suggestion]
    else
      return [false, nil]
    end

  end

  def post_suggestions(suggestions)
    puts "post_suggestions: #{suggestions.count}"
    url = "https://gitee.com/api/v5/repos/#{@repo}/pulls/#{@pr_iid}/comments"
    suggestions.each do |suggestion|
      RestClient.post url, suggestion
    end
  end

  def get_exists_suggestions
    return @exists_suggestions unless @exists_suggestions.nil?
    puts "get_exists_suggestions"
    suggestions = []
    url = "https://gitee.com/api/v5/repos/#{@repo}/pulls/#{@pr_iid}/comments?access_token=#{@access_token}&page=1&per_page=100&comment_type=diff_comment"
    response_json = RestClient.get url
    suggestions.concat(suggestion_response_to_array(response_json))
    if response_json.headers[:total_page].to_i > 1
      (2..response_json.headers[:total_page].to_i).each do |page|
        url = "https://gitee.com/api/v5/repos/#{@repo}/pulls/#{@pr_iid}/comments?access_token=#{@access_token}&page=#{page}&per_page=100&comment_type=diff_comment"
        response_json = RestClient.get url
        response = JSON.parse(response_json.body)
        suggestions.concat(suggestion_response_to_array(response_json))
      end
    end
    @exists_suggestions = suggestions
    suggestions
  end

  def suggestion_response_to_array(response_json)
    suggestions = []
    response = JSON.parse(response_json.body)
    response.map do |r|
      next if r['path'].nil? || r['commit_id'].nil? || r['new_line'].nil?
      next unless r['body'].include?("Rubocop suggestion\n```suggestion\n")
      suggestions.push({
        path: r['path'],
        commit_id: r['commit_id'],
        new_line: r['new_line'],
        body: r['body']
      })
    end
    suggestions
  end
end

inputted_strings = ARGV
if ARGV.empty?
  puts "nothing :("
else
  repo, head_sha, bare_sha, pr_iid, access_token_str = inputted_strings
  puts "repo: #{repo}, head_sha: #{head_sha}, bare_sha: #{bare_sha}"
  RubocopAnalysis.new(repo, head_sha, bare_sha, pr_iid, access_token_str).exec
end
